package com.sparkseries.redis;

import com.sparkseries.common.log.domain.BaseLog;
import com.sparkseries.common.util.redis.RedisKeys;
import com.sparkseries.common.util.redis.RedisUtils;
import com.sparkseries.common.util.tool.ObjectConvertTool;
import com.sparkseries.dao.SysNoticeMapper;
import com.sparkseries.domain.MyNotice;
import com.sparkseries.domain.SysLog;
import com.sparkseries.domain.SysNotice;
import com.sparkseries.dto.SysNoticeDTO;
import com.sparkseries.service.SysLogService;
import com.sparkseries.service.SysNoticeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class Consumer implements CommandLineRunner {

    @Resource
    private SysLogService logService;
    @Resource
    private SysNoticeService sysNoticeService;
    @Resource
    private SysNoticeMapper sysNoticeMapper;
    @Resource
    private RedisUtils redisUtils;

    /**
     * 用于执行定时任务和周期性任务的线程池服务,创建一个单线程的定时任务执行器,该线程为守护线程
     */
    private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1,
            new BasicThreadFactory.Builder().namingPattern("consumer-%d").daemon(true).build());

    // 读取每次消费的数量，默认值为 100
    @Value("${consumer.insert.count:100}")
    private int insertCount;

    // 读取任务执行间隔，默认值为 10 秒
    @Value("${consumer.interval:10}")
    private long taskInterval;

    @Override
    public void run(String... args) {
        try {
            scheduleTask("日志", this::saveLog);
            scheduleTask("我的通知", this::saveMyNotice);
            scheduleTask("通知", this::saveNotice);
        } catch (Exception e) {
            log.error("安排任务失败", e);
        }
    }

    private void scheduleTask(String taskType, Runnable task) {
        executor.scheduleWithFixedDelay(task, 0, taskInterval, TimeUnit.SECONDS);
        log.info("{} 消费者任务已安排，执行间隔为 {} 秒", taskType, taskInterval);
    }

    private void saveLog() {
        consumeFromRedis(RedisKeys.getLogKey(), BaseLog.class, item -> {
            SysLog sysLog = ObjectConvertTool.originConvert(item, SysLog.class);
            logService.save(sysLog);
            String logLevel = sysLog.getLogLevel();
            if ("ERROR".equals(logLevel)) {
                logMonitor(sysLog);
            }
        });
    }

    // 有异常日志时给管理员发消息
    private void logMonitor(SysLog sysLog) {
        SysNoticeDTO sysNoticeDTO = new SysNoticeDTO();
        sysNoticeDTO.setNoticeType(3);
        sysNoticeDTO.setNoticeLevel(2);
        sysNoticeDTO.setNoticeTitle("错误日志");
        sysNoticeDTO.setNoticeContent(sysLog.getErrorInfo());
        sysNoticeDTO.setReceiveType(1);
        sysNoticeDTO.setReceiveIds("3");
        sysNoticeDTO.setReceiveNames("zhengshanwei");
        sysNoticeDTO.setStatus(0);
        sysNoticeDTO.setFileFlag(0);
        sysNoticeService.createNotice(sysNoticeDTO);
    }

    private void saveMyNotice() {
        consumeFromRedis(RedisKeys.getMyNoticeKey(), MyNotice.class, sysNoticeMapper::insertNoticeToUser);
    }

    private void saveNotice() {
        consumeFromRedis(RedisKeys.getSysNoticeKey(), SysNotice.class, sysNoticeMapper::createNotice);
    }

    private <T> void consumeFromRedis(String key, Class<T> clazz, C<T> consumer) {
        try {
            for (int i = 0; i < insertCount; i++) {
                T item = (T) redisUtils.rightPop(key);
                if (item == null) {
                    log.debug("Redis 队列 {} 中没有更多信息，退出循环", key);
                    break;
                }
                consumer.accept(item);
            }
        } catch (Exception e) {
            log.error("从 Redis 队列 {} 保存信息时发生错误", key, e);
        }
    }

    @FunctionalInterface
    private interface C<T> {
        void accept(T t);
    }
}
